package net.sashag.nadlandroid;

import java.io.ByteArrayOutputStream;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;

import android.app.AlertDialog;
import android.content.Context;
import android.content.DialogInterface;
import android.content.DialogInterface.OnClickListener;
import android.content.Intent;
import android.graphics.Bitmap;
import android.graphics.Bitmap.CompressFormat;
import android.graphics.BitmapFactory;
import android.net.Uri;
import android.util.Log;
import android.widget.Toast;

import com.google.android.apps.analytics.easytracking.EasyTracker;
import com.parse.GetCallback;
import com.parse.ParseException;
import com.parse.ParseFile;
import com.parse.ParseGeoPoint;
import com.parse.ParseObject;
import com.parse.ParseQuery;
import com.parse.ParseQuery.CachePolicy;
import com.parse.ParseUser;

public class Apartment implements Serializable {

	private static final long serialVersionUID = -7249271664111158192L;

	private String address;
	private int bedrooms;
	private int price;
	private double latitude;
	private double longitude;
	private boolean hasLift;
	private boolean hasAC;
	private boolean hasParking;
	private String notes;
	private String email;
	private String phone;
	private String userObjectId;
	private String objectId;
	private boolean hasPictures;
	private boolean published = true;
	private ArrayList<Bitmap> pictures = new ArrayList<Bitmap>();
	
	public static Apartment fromId(String apartmentId) throws ParseException {
		ParseQuery query = new ParseQuery("Apartment");
		query.setCachePolicy(CachePolicy.CACHE_ELSE_NETWORK);
		query.setMaxCacheAge(1000*60*60 /*one hour*/);
		ParseObject apartmentObject = query.get(apartmentId);
		return fromParseObject(apartmentObject);
	}
	
	public static Apartment fromParseObject(ParseObject apartmentObject) {
		//TODO: This whole serialization/deserialization to ParseObject business
		//		can pretty easily be automated with some annotations (ORM-style)
		Apartment apartment = new Apartment();
		apartment.objectId = apartmentObject.getObjectId();
		apartment.userObjectId = apartmentObject.getString("UserObjectId");
		apartment.setAddress(apartmentObject.getString("Address"));
		apartment.setBedrooms(apartmentObject.getInt("Bedrooms"));
		apartment.setEmail(apartmentObject.getString("Email"));
		apartment.setHasAC(apartmentObject.getBoolean("AC"));
		apartment.setHasLift(apartmentObject.getBoolean("Lift"));
		apartment.setHasParking(apartmentObject.getBoolean("Parking"));
		ParseGeoPoint geopoint = apartmentObject.getParseGeoPoint("Location");
		apartment.setLatitude(geopoint.getLatitude());
		apartment.setLongitude(geopoint.getLongitude());
		apartment.setNotes(apartmentObject.getString("Notes"));
		apartment.setPhone(apartmentObject.getString("Phone"));
		apartment.setPrice(apartmentObject.getInt("Price"));
		if (apartmentObject.has("Published")) {
			apartment.published = apartmentObject.getBoolean("Published");
		} else {
			apartment.published = true; //Old apartments might not have 'Published' information
		}
		apartment.hasPictures = apartmentObject.getInt("NumPictures") > 0;
		//Note: pictures are retrieved on demand, not when creating the object
		return apartment;
	}
	
	public static Apartment[] getOwnedApartments(String userId) throws ParseException {
		ParseQuery query = new ParseQuery("Apartment");
		query.whereEqualTo("UserObjectId", userId);
		List<ParseObject> apartmentObjects = query.find();
		Apartment[] apartments = new Apartment[apartmentObjects.size()];
		for (int i = 0; i < apartmentObjects.size(); ++i) {
			apartments[i] = Apartment.fromParseObject(apartmentObjects.get(i));
		}
		return apartments;
	}
	
	//This method might take a long time to execute
	public Bitmap[] getPictures() throws ParseException {
		ParseQuery query = new ParseQuery("Apartment");
		query.setCachePolicy(CachePolicy.CACHE_ELSE_NETWORK);
		query.setMaxCacheAge(1000*60*60 /*one hour*/);
		ParseObject apartmentObject = query.get(objectId);
		int numPictures = apartmentObject.getInt("NumPictures");
		Bitmap[] bitmaps = new Bitmap[numPictures];
		for (int i = 0; i < numPictures; ++i) {
			ParseFile pictureFile = (ParseFile)apartmentObject.get("Picture_" + i);
			byte[] pictureData = pictureFile.getData();
			bitmaps[i] = BitmapFactory.decodeByteArray(pictureData, 0, pictureData.length);
		}
		return bitmaps;
	}
	
	public void saveAsParseObject(ProgressReporter reporter) {
		ParseFile[] files = new ParseFile[pictures.size()];
		for (int i = 0; i < pictures.size(); ++i) {
			ByteArrayOutputStream stream = new ByteArrayOutputStream();
			pictures.get(i).compress(CompressFormat.PNG, 50, stream);
			files[i] = new ParseFile(stream.toByteArray());
			try {
				files[i].save();
			} catch (ParseException e) {
				reporter.error(e);
				return;
				//TODO: Delete all the files uploaded so far
			}
			reporter.progress(i, pictures.size());
		}
		
		ParseGeoPoint geoPoint = new ParseGeoPoint(latitude, longitude);
		ParseObject newApartment = new ParseObject("Apartment");
		newApartment.put("UserObjectId", userObjectId = ParseUser.getCurrentUser().getObjectId());
		newApartment.put("Address", getAddress());
		newApartment.put("Bedrooms", getBedrooms());
		newApartment.put("Price", getPrice());
		newApartment.put("Location", geoPoint);
		newApartment.put("Lift", hasLift);
		newApartment.put("AC", hasAC);
		newApartment.put("Parking", hasParking);
		newApartment.put("Notes", notes);
		newApartment.put("Email", email);
		newApartment.put("Phone", phone);
		newApartment.put("Published", published);
		newApartment.put("NumPictures", files.length);
		for (int i = 0; i < files.length; ++i) {
			newApartment.put("Picture_" + i, files[i]); 
		}
		try {
			newApartment.save();
			objectId = newApartment.getObjectId();
			Log.v("Apartment", "Apartment saved with object id: " + objectId);
		} catch (ParseException e) {
			reporter.error(e);
			return;
		}
		reporter.done();
	}
	
	public void showContactOptions(final Context context) {
		AlertDialog.Builder builder = new AlertDialog.Builder(context);
		builder.setTitle("Contact Apartment Owner");
		final ArrayList<String> contactOptions = new ArrayList<String>();
		if (getEmail() != null && getEmail().length() > 0) {
			contactOptions.add(getEmail());
		}
		if (getPhone() != null && getPhone().length() > 0) {
			contactOptions.add(getPhone());
		}
		contactOptions.add("Through Nadlandroid");
		builder.setItems(contactOptions.toArray(new String[contactOptions.size()]), new OnClickListener() {
			public void onClick(DialogInterface dialog, int which) {
				String selectedOption = contactOptions.get(which);
				if (selectedOption.equals(getPhone())) {
					Intent dialIntent = new Intent(Intent.ACTION_DIAL);
					dialIntent.setData(Uri.parse("tel:" + getPhone()));
					context.startActivity(dialIntent);
				} else if (selectedOption.equals(getEmail())) {
					Intent emailIntent = new Intent(Intent.ACTION_SENDTO);
					emailIntent.setData(Uri.parse(getEmail()));
					context.startActivity(emailIntent);
				} else {
					NadlandroidPushChatMessage push = new NadlandroidPushChatMessage(objectId, userObjectId);
					push.send();
					Toast.makeText(context, "A message was sent to the apartment owner.", Toast.LENGTH_LONG).show();
				}
				dialog.dismiss();
			}
		});
		builder.create().show();
		EasyTracker.getTracker().trackPageView("Contact Menu");
	}
	
	public boolean isWithinRegion(Region region) {
		double latNE = region.getNorthEast().getLatitude();
		double lonNE = region.getNorthEast().getLongitude();
		double latSW = region.getSouthWest().getLatitude();
		double lonSW = region.getSouthWest().getLongitude();
		return (latitude >= latSW && latitude <= latNE) && (longitude >= lonSW && longitude <= lonNE);
	}
	
	public void setAddress(String address) {
		this.address = address;
	}

	public String getAddress() {
		return address;
	}

	public void setBedrooms(int bedrooms) {
		this.bedrooms = bedrooms;
	}

	public int getBedrooms() {
		return bedrooms;
	}

	public void setPrice(int price) {
		this.price = price;
	}

	public int getPrice() {
		return price;
	}

	public void setLatitude(double latitude) {
		this.latitude = latitude;
	}

	public double getLatitude() {
		return latitude;
	}

	public void setLongitude(double longitude) {
		this.longitude = longitude;
	}

	public double getLongitude() {
		return longitude;
	}

	public void setHasLift(boolean hasLift) {
		this.hasLift = hasLift;
	}

	public boolean isHasLift() {
		return hasLift;
	}

	public void setHasAC(boolean hasAC) {
		this.hasAC = hasAC;
	}

	public boolean isHasAC() {
		return hasAC;
	}

	public void setHasParking(boolean hasParking) {
		this.hasParking = hasParking;
	}

	public boolean isHasParking() {
		return hasParking;
	}

	public void setNotes(String notes) {
		this.notes = notes;
	}

	public String getNotes() {
		return notes;
	}

	public void setEmail(String email) {
		this.email = email;
	}

	public String getEmail() {
		return email;
	}

	public void setPhone(String phone) {
		this.phone = phone;
	}

	public String getPhone() {
		return phone;
	}
	
	public void addPicture(Bitmap picture) {
		pictures.add(picture);
	}
	
	public String getUserObjectId() {
		return userObjectId;
	}
	
	public String getObjectId() {
		return objectId;
	}
	
	public boolean hasPictures() {
		return hasPictures;
	}

	public boolean isPublished() {
		return published;
	}
	
	public void updatePublishStateAndSaveInBackground(final boolean newPublishState) {
		if (published == newPublishState)
			return; //Nothing to do
		
		ParseQuery query = new ParseQuery("Apartment");
		query.setCachePolicy(CachePolicy.CACHE_ELSE_NETWORK);
		query.setMaxCacheAge(1000*60*60 /*one hour*/);
		query.getInBackground(objectId, new GetCallback() {
			public void done(ParseObject apartmentObject, ParseException e) {
				if (e != null || apartmentObject == null) {
					//We don't want to report this error; just keep the apartment in
					//its previous publish state.
					Log.e("Apartment", "Error retrieving apartment object for update: " +
							(e != null ? e.getMessage() : "unspecified error"));
				} else {
					apartmentObject.put("Published", newPublishState);
					apartmentObject.saveInBackground();
					published = newPublishState;
				}
			}
		});
	}
	
	public String formatForSend() {
		return String.format(
				"I've just seen an apartment on Nadlandroid and wanted to share it with you.\n\n" +
				"Bedrooms: %d\nPrice: %d\n" +
				"Address: %s\n\nOwner email: %s\nOwner phone: %s\n\n" +
				"Apartment details: http://nadlandroid.azurewebsites.net/apartment/%s\n" +
				"Nadlandroid: http://nadlandroid.azurewebsites.net",
				getBedrooms(), getPrice(), getAddress(), getEmail(), getPhone(), getObjectId()
		);
	}
	
}
